/**
 * This class sits between the agent manager and the 
 * connection manager. It provides an incoming and an
 * outgoing queue using vectors, and implements the 
 * agents toString and fromString methods as required. 
 * 
 * @author Aidan
 */
package ap;
import java.util.Vector;
import comms.ConnectionManager;
import agent.*;

public class AgentPlatform implements Runnable{
	private manager.IOHandlerManager agentManager;
	private ConnectionManager connectionManager;
	private Vector inAgent;
	private Vector outAgent;
	private AgentPkt agentPkt;
	private String udpPort;
	private String tcpPort;
	private boolean quit;    
	/**
	 * constructor
	 * @param am : reference back to the agent manager that instantiated this object 
	 */
	public AgentPlatform(manager.IOHandlerManager am, String udpPort, String tcpPort){
		this.agentManager = am;
		//this.connectionManager = new ConnectionManager(this);
		
		this.inAgent = new Vector();
		this.outAgent = new Vector();
		this.agentPkt = new AgentPkt();
		this.udpPort = udpPort;
		this.tcpPort = tcpPort;
		this.quit = false;
		this.start();
	}
	/**
	 * used to start this agent platform thread
	 */
	public void start() {
		Thread t = new Thread(this);
		t.start();
	}
	/**
	 * wait for agents to be set from the agent manager or the connection manager
	 * then translate the string/agent 
	 */
	public synchronized void run(){
		try{
			System.err.println("UDP Port: " + this.udpPort + " TCP Port: " + this.tcpPort);
			this.connectionManager = new ConnectionManager(this, this.udpPort, this.tcpPort);
		}catch (NullPointerException e){
			System.err.println("Error thrown when trying to create connection manager");
			e.printStackTrace();
		}
		while(!quit){			
			if(this.inAgent.isEmpty()&& this.outAgent.isEmpty()) {
				try{
					System.err.println("Platform: waiting");
					wait();
				}catch(InterruptedException e){
					System.err.println("Interrupted Exceprion thrown in AgentPlatform.run.wait()");
					e.printStackTrace();
				}catch (NullPointerException e){
					System.err.println("Interrupted Exceprion thrown in AgentPlatform.run.wait()");
					e.printStackTrace();
				}
			}
			else if(!this.inAgent.isEmpty()) {
				if(!this.agentManager.isAgentWaiting()){
					System.err.println("AP has agent waiting");
					this.agentManager.agentWaiting();
					System.err.println("AP notified AM of new agent");
				}
				try {
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			}		
			else if(!this.outAgent.isEmpty()) {	
				this.agentPkt = (AgentPkt)outAgent.firstElement();
				this.outAgent.removeElementAt(0);
				this.connectionManager.forwardAgent(agentPkt);
			}
		}	
	}
	/****************************************************************
	 * access methods used by agent manager and connection manager
	 ****************************************************************
	 * called by agent manager to pass up received agent
	 * 
	 * @return the agent from the inAgent vector
	 */
	public synchronized Agent getInAgent() throws NoSuchElementException{
		System.err.println("AP.getInAgent() method called");
		if(inAgent.isEmpty())
			throw new NoSuchElementException("inAgent vector Empty");
		else{
			Agent agent;		
			agent = (Agent)inAgent.firstElement();
			inAgent.removeElementAt(0);
			
			return agent;			
		}
	}
	/****************************************************************
	 * called by agent manager to set agent to be sent out
	 * 		set agent string
	 * 		set destination address
	 * 		set destination port
	 * 		set destination type
	 * @param outAgent the outAgent to set
	 */
	public synchronized void setOutAgent(Agent outAgent) {
		if(outAgent.getType().equals(agent.Agent.TYPE_MEETING)){
			this.agentPkt.setAgentString(outAgent.toString());
			System.err.println("PKT: Agent stream set: " + outAgent.toString());
			this.agentPkt.setDestAddress(outAgent.getNextAddress());
			System.err.println("PKT: Agent address set: " + outAgent.getNextAddress());
			this.agentPkt.setDestPort(outAgent.getNextPort());
			System.err.println("PKT: Agent port set: " + outAgent.getNextPort());
			outAgent.incrementToNextAddress();
		}else{		
			this.agentPkt.setDestAddress(outAgent.getNextAddress());
			System.err.println("PKT: Agent address set: " + outAgent.getNextAddress());
			this.agentPkt.setDestPort(outAgent.getNextPort());
			System.err.println("PKT: Agent port set: " + outAgent.getNextPort());
			outAgent.incrementToNextAddress();
			this.agentPkt.setAgentString(outAgent.toString());
			System.err.println("PKT: Agent stream set: " + outAgent.toString());
		}
		
		//if(outAgent.getConnectionType() == MeetingAgent.TYPE_DATAGRAM)
		//	this.agentPkt.setDestType_Datagram();
		//else
		//	this.agentPkt.setDestType_Socket();		
	
		this.outAgent.addElement(agentPkt);		
		notify();
	}		
	/****************************************************************
	 * called by connection manager to get agent to be sent out
	 * 
	 * @return the agentPkt from the outAgent vector
	 */
/*	public synchronized AgentPkt getOutAgent() throws NoSuchElementException{
		if(outAgent.isEmpty())
			throw new NoSuchElementException("outAgent vector Empty");
		else{
			AgentPkt agentPkt;
			agentPkt = (AgentPkt)outAgent.firstElement();
			outAgent.removeElementAt(0);		
			
			return agentPkt;			
		}
	}
*/	
	/****************************************************************
	 * called by connection manager to set the received agent
	 * 
	 * @param add the agent to inAgent vector
	 */
	public synchronized void setInAgent(String agentString) {
		Tokenizer tokenizer= new Tokenizer(agentString,"|");
		String token = "";
		System.err.println("setInAgent here");
		try{
			token = tokenizer.nextToken();
		}catch(NoSuchElementException e){
			e.printStackTrace();
		}
		if(token.equals(agent.Agent.TYPE_MEETING)){
			MeetingAgent agent = new MeetingAgent();
			agent.fromString(agentString);
			this.inAgent.addElement(agent);
		}else if(token.equals(agent.Agent.TYPE_INFORMATION)){
			//create agent and add to vector
			InfoAgent agent = new InfoAgent();
			agent.fromString(agentString);
			this.inAgent.addElement(agent);
		}
		notify();
	}
	/****************************************************************
	 * method to stop the current agent platform thread
	 */
	public synchronized void stop() {
		this.quit = true;
		notify();
	}
}